package com.jweb.framework.ws;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.wsdl.Definition;
import javax.wsdl.Import;
import javax.wsdl.Message;
import javax.wsdl.Operation;
import javax.wsdl.Part;
import javax.wsdl.Port;
import javax.wsdl.PortType;
import javax.wsdl.Service;
import javax.wsdl.extensions.ExtensibilityElement;
import javax.wsdl.extensions.soap.SOAPAddress;
import javax.wsdl.extensions.soap12.SOAP12Address;
import javax.xml.namespace.QName;

import org.apache.ws.commons.schema.XmlSchemaCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import com.jweb.framework.ws.channel.SchemaValidator;
import com.jweb.framework.ws.channel.ServiceDefinition;
import com.jweb.framework.ws.channel.ServiceManager;
import com.jweb.framework.ws.channel.ServiceManagerAware;
import com.jweb.framework.ws.sdo.SdoUtils;
import com.jweb.framework.ws.soap.addressing.client.WSAddressingHead;
import com.jweb.framework.ws.support.CommonsXsdSchemaCollectionJWeb;
import com.jweb.framework.ws.wsdl.WsdlWebServiceDefinition;

/**
 * 修改人： Thomas
 * 修改内容：新增 
 * 类说明：.WSDL 文件解析
 */
public class DefaultServiceManager implements ServiceManager,InitializingBean,ApplicationContextAware{
	
	private static Logger logger = LoggerFactory.getLogger(DefaultServiceManager.class);
	private ApplicationContext applicationContext; 
	
	private Map serviceIdMap ;
	
	private Map locationMap ;
	
	private Map schemaValidatorMap ;
	
	private Map operationMap ;
	
	private Map messageMapperMap ;
	
	private Map useAddressingMap ;
	
	private Map wsAddressingHeadMap ;
	
	private boolean useSdo ;
	
	//private Mapping	mapping ;
	
	public DefaultServiceManager(){
		serviceIdMap = new HashMap();
		locationMap = new HashMap();
		schemaValidatorMap = new HashMap();
        operationMap = new HashMap();
        messageMapperMap = new HashMap();
        useAddressingMap = new HashMap();
        wsAddressingHeadMap = new HashMap();
        //mapping = new SimpleMapping();
	}
	
	public void setUseSdo(boolean flag){
		this.useSdo = flag ;
	}
	
	/*public void setXmlMapping(Mapping mapping){
		this.mapping = mapping ;
	}*/
	
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext ;
	}
	
	public void afterPropertiesSet() throws Exception {
		Map map = this.applicationContext.getBeansOfType(ServiceManagerAware.class); 
		for(Iterator it = map.values().iterator() ; it.hasNext() ; ((ServiceManagerAware)it.next()).setServiceManager(this));
	}
	// "servicedefinition"=WsdlWebServiceDefinition
	public void injectServiceDefinition(ServiceDefinition servicedefinition) throws Exception {
		
		ServiceDefinition servicedefinition1 = servicedefinition ;
		String s = servicedefinition.getBeanName() ; 
		
		Definition definition = servicedefinition1.getDefinition();
		String s1 = null ;
		boolean flag = false ;
		WSAddressingHead wsAddressingHead = null ;
		
		if(servicedefinition1 instanceof WsdlWebServiceDefinition){
			WsdlWebServiceDefinition wsdlWebServiceDefinition = (WsdlWebServiceDefinition) servicedefinition1 ;
			if(wsdlWebServiceDefinition.getLocation() != null){
				s1 = wsdlWebServiceDefinition.getLocation() ; 
			}
			flag = wsdlWebServiceDefinition.isUseAddressing() ; 
			wsAddressingHead = wsdlWebServiceDefinition.getWsAddressingHead(); 
		}
		
		if(s1 == null){ 
			s1 = invoke(definition); 
		}
		
		SchemaValidator schemaValidator = new SchemaValidator(servicedefinition1.getSchemaCollection().createValidator());
		XmlSchemaCollection xmlSchemaCollection = ((CommonsXsdSchemaCollectionJWeb)servicedefinition1.getSchemaCollection()).getSchemasCollection();
		
		if(this.useSdo){// true
			SdoUtils.defineSdoTypes(s, servicedefinition1.getSchemaCollection().getXsdSchemas());
		}
		
		invoke(s , definition , s1 , schemaValidator , xmlSchemaCollection , flag , wsAddressingHead);
	}

	private void invoke(String s, Definition definition, String s1,
			SchemaValidator schemaValidator,
			XmlSchemaCollection xmlSchemaCollection, boolean flag,
			WSAddressingHead wsAddressingHead)  throws Exception {
		
		Map map = definition.getImports(); // map=null
		for(Iterator it = map.values().iterator() ; it.hasNext() ;){// false
			List list = (List) it.next();
			for(int l = 0 ; l < list.size() ; l++){
				Import import1 = (Import) list.get(l);
				invoke(s , import1.getDefinition() , s1 , schemaValidator , xmlSchemaCollection , flag , wsAddressingHead);
			}
		}
		
		//MapperFactory mapperFactory = this.useSdo ? null : new MapperFactory(this.mapping, xmlSchemaCollection, definition.getNamespaces()); // mapperFactory=null
		Map map1 = definition.getPortTypes();
		for(Iterator it1 = map1.values().iterator() ; it1.hasNext() ;){
			PortType portType = (PortType) it1.next();
			for(Iterator it2 = portType.getOperations().iterator() ; it2.hasNext() ;){
				Operation operation = (Operation) it2.next();
				invoke(s , operation.getInput().getMessage() , s1 , schemaValidator , operation /*, mapperFactory*/ , flag  , wsAddressingHead);
				if(operation.getOutput() != null){
					invoke(s , operation.getOutput().getMessage() , s1 , schemaValidator , operation /*, mapperFactory*/ , flag , wsAddressingHead);
				}
			}
		}
	}

	private void invoke(String s, Message message, String s1,
			SchemaValidator schemaValidator, Operation operation,
			/*MapperFactory mapperFactory,*/ boolean flag,
			WSAddressingHead wsAddressingHead) throws Exception {
		
		/*MessageMapper messageMapper = mapperFactory == null ? null : mapperFactory.createMessageMapper(message);*/ // messageMapper=null
		Collection collection = message.getParts().values();
		if(collection.size() != 1){
			throw new IllegalArgumentException("document_literal_wrap require only one part in message");
		}
		Part part = (Part) collection.iterator().next();
		QName qname = part.getElementName();
		if(qname == null){
			throw new IllegalArgumentException("document_literal_wrap require message part use element only");
		}
		
		this.operationMap.put(qname, operation);
		
		String s2 = (String) this.serviceIdMap.put(qname, s);
		
		this.locationMap.put(qname, s1);
		
		this.schemaValidatorMap.put(qname, schemaValidator);
		
		this.useAddressingMap.put(qname, new Boolean(flag));
		
		if(wsAddressingHead != null){ 
			this.wsAddressingHeadMap.put(qname, wsAddressingHead);
		}
		/*if(messageMapper != null){ 
			this.messageMapperMap.put(qname, messageMapper);
		}*/
		
		if(s2 != null){
			logger.warn("duplicated element defined : {} in service {} and {} " , new Object[]{qname , s , s2});
		}
		
	}

	private String invoke(Definition definition) {
		
		String s = null ;
		Service service = (Service) definition.getServices().values().iterator().next();
		Map map = service.getPorts();
		if(map.size() == 1){
			Port port = (Port) map.values().iterator().next();
			ExtensibilityElement extensibilityElement = (ExtensibilityElement) port.getExtensibilityElements().get(0);
			if(extensibilityElement instanceof SOAPAddress){
				s = ((SOAPAddress)extensibilityElement).getLocationURI(); 
			}
			else {
				if(extensibilityElement instanceof SOAP12Address){
					s = ((SOAP12Address)extensibilityElement).getLocationURI();
				}
				else {
					throw new IllegalStateException((new StringBuilder("unsupported address: ")).append(extensibilityElement.getElementType()).toString());
				}
			}
		}
		else {
			throw new IllegalStateException("only one port required in wsdl's service");
		}
		return s ;
	}
	
	
	public String getServiceId(QName qname){
		return (String) this.serviceIdMap.get(qname);
	}
	
	public String getLocation(QName qname){
		return (String) this.locationMap.get(qname);
	}
	
	public boolean isOneway(QName qname){
		Operation operation = (Operation) this.operationMap.get(qname);
		if(operation == null){
			throw new IllegalStateException((new StringBuilder("no operation defined for : ")).append(qname).toString());
		}
		return operation.getOutput() == null ;
	}
	
	public boolean isUseAddressing(QName qname){
		return ((Boolean)this.useAddressingMap.get(qname)).booleanValue();
	}
	
	public WSAddressingHead getWsAddressingHead(QName qname){
		return (WSAddressingHead) this.wsAddressingHeadMap.get(qname);
	}
	
	public SchemaValidator getValidator(QName qname){
		return (SchemaValidator) this.schemaValidatorMap.get(qname);
	}
	
	/*public MessageMapper getMessageMapper(QName qname){
		return (MessageMapper) this.messageMapperMap.get(qname);
	}*/
	
}